package main;

import main.Parser.ParseException;
import utils.*;

public class Camera implements Parseable {

    private Point3D eye = null;
    private Vector direction = null;
    private Point3D lookAt = null;
    private Vector upDirection = null;
    private Vector rightDirection = null;
    private double screenDist = 0;
    private double screenWidth = 2.0;
    private Point3D screenCenter = null;

    public Camera() {
    }

    @Override
    public boolean setParameter(String name, String[] args) throws ParseException {
	if (name.equals("eye")) {
	    if (args.length != 3) {
		return false;
	    }
	    eye = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("look-at")) {
	    if (args.length != 3) {
		return false;
	    }
	    lookAt = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("screen-dist")) {
	    if (args.length != 1) {
		return false;
	    }
	    screenDist = Double.parseDouble(args[0]);
	    return true;
	} else if (name.equals("screen-width")) {
	    if (args.length != 1) {
		return false;
	    }
	    screenWidth = Double.parseDouble(args[0]);
	    return true;
	} else if (name.equals("up-direction")) {
	    if (args.length != 3) {
		return false;
	    }
	    upDirection = new Vector(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("direction")) {
	    if (args.length != 3) {
		return false;
	    }
	    direction = new Vector(Parser.stringArrToDoubleArr(args));
	    return true;
	}

	// We didn't find any parameter
	return false;
    }

    public Point3D getScreenPoint(double x, double y, double canvasWidth, double canvasHeight) {

	Point3D screenPoint;

	double factorWidth = screenWidth / canvasWidth;
	double upDistanceFromCenter = (y - (canvasHeight / 2)) * factorWidth * -1;
	double rightDistanceFromCenter = (x - (canvasWidth / 2)) * factorWidth;

	screenPoint = screenCenter.addVector(upDirection.scale(upDistanceFromCenter));
	screenPoint = screenPoint.addVector(rightDirection.scale(rightDistanceFromCenter));

	return screenPoint;
    }

    public boolean finalizeObject() {

	if (upDirection == null || eye == null || screenDist <= 0) {
	    return false;
	}

	if (direction == null) {
	    if (lookAt == null) {
		return false;
	    } else {
		direction = new Vector(eye, lookAt);
	    }
	}

	direction = direction.normalize();

	// calculates the real up direction
	upDirection = upDirection.normalize();
	rightDirection = upDirection.crossProduct(direction);

	if (rightDirection.isZero()) {
	    return false;
	} else {
	    rightDirection = rightDirection.scale(-1).normalize();
	}

	upDirection = rightDirection.crossProduct(direction);

	// calculates the screen center
	screenCenter = eye.addVector(direction.scale(screenDist));

	return true;
    }

    public boolean isCamera() {
	return true;
    }

    public boolean isLight() {
	return false;
    }

    public Ray buildRayTowards(Point3D p) {
	return new Ray(eye, p);
    }

    public Point3D getEyePosition() {
	return new Point3D(eye);
    }

}
